package com.yuan.util.object;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;
import com.yuan.util.string.StrUtil;
import com.yuan.util.string.WordUtil;

import java.io.*;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 对象工具类<br>
 * date：2017/12/14 14:30
 *
 * @author YouYuan
 * @version 1.0
 */
public class ObjUtil {

    /**
     * 缓存Method对象,避免重复反射获取,提高效率
     */
    private static final Map<Class, Map<String,Method>> CLASS_METHOD_CACHE = new ConcurrentHashMap<Class, Map<String,Method>>();

    /**
     * 已知属性名，遵循javaBean规范返回get方法名。如属性名是name,get方法是getName
     * @param attrName
     * @return
     */
    public static String getGetMethodName(String attrName) {
        if (StrUtil.isEmpty(attrName)) {
            return "";
        }
        StringBuilder mbuffer = new StringBuilder();
        if (attrName.length() > 1 && Character.isUpperCase(attrName.charAt(1))) {
            mbuffer.append("get").append(attrName);
        } else {
            mbuffer.append("get").append(WordUtil.initialUp(attrName));
        }
        return mbuffer.toString();
    }

    /**
     * 已知属性名，遵循javaBean规范返回set方法名。如属性名是name,set方法是setName
     * @param attrName
     * @return
     */
    public static String getSetMethodName(String attrName) {
        if (StrUtil.isEmpty(attrName)) {
            return "";
        }
        StringBuilder mbuffer = new StringBuilder();
        if (attrName.length() > 1 && Character.isUpperCase(attrName.charAt(1))) {
            mbuffer.append("set").append(attrName);
        } else {
            mbuffer.append("set").append(WordUtil.initialUp(attrName));
        }
        return mbuffer.toString();
    }

    /**
     * 已知属性名，遵循javaBean规范返回boolean的is方法名，如属性名是boy,is方法是isBoy，属性名是以is开头并且后面跟随着大写字母，例如isX则返回isX
     * @param attrName
     * @return
     */
    public static String getBooIsMethodName(String attrName) {
        StringBuilder mbuffer = new StringBuilder();
        if (attrName.startsWith("is") && attrName.length() > 2 && Character.isUpperCase(attrName.charAt(2))) {
            mbuffer.append(attrName);
        } else {
            mbuffer.append("is").append(WordUtil.initialUp(attrName));
        }
        return mbuffer.toString();
    }

    /**
     * 已知属性名，遵循javaBean规范返回boolean的set方法名，如属性名是boy,set方法是setBoy，属性名是以is开头并且后面跟随着大写字母，例如isX则返回setX
     * @param attrName
     * @return
     */
    public static String getBooSetMethodName(String attrName) {
        StringBuilder mbuffer = new StringBuilder();
        if (attrName.startsWith("is") && attrName.length() > 2 && Character.isUpperCase(attrName.charAt(2))) {
            mbuffer.append("set").append(attrName.substring(2));
        } else {
            mbuffer.append("set").append(WordUtil.initialUp(attrName));
        }
        return mbuffer.toString();
    }

    /**
     * 获取bean对象中指定属性的值
     * @param bean
     * @param propertyName
     * @return
     */
    public static Object getBeanProperty(Object bean, String propertyName) {
        if (bean == null) {
            return null;
        }
        Class c = bean.getClass();
        Map<String, Method> methodMap = CLASS_METHOD_CACHE.get(c);
        if (methodMap == null) {
            methodMap = new HashMap<String, Method>(3);
            CLASS_METHOD_CACHE.put(c, methodMap);
        }
        Method method = methodMap.get(propertyName);
        if (method == null) {
            String methodName = getGetMethodName(propertyName);
            method = getGetMethod(c, methodName, null);
            if (method == null) {
                methodName = getBooIsMethodName(propertyName);
                method = getGetMethod(c, methodName, null);
            }
            methodMap.put(propertyName, method);//缓存Method对象
        }
        if (method != null) {
            try {
                return method.invoke(bean, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 获取bean对象中指定属性的值,支持链式获取,例如user.child.name
     * @param bean
     * @param propertyName
     * @return
     */
    public static Object getChainBeanProperty(Object bean, String propertyName) {
        if (bean == null || propertyName == null) {
            return null;
        }
        String[] names = propertyName.split("\\.");
        for (int i = 0; i < names.length; i++) {
            propertyName = names[i];
            Class c = bean.getClass();
            Map<String, Method> methodMap = CLASS_METHOD_CACHE.get(c);
            if (methodMap == null) {
                methodMap = new HashMap<String, Method>(3);
                CLASS_METHOD_CACHE.put(c, methodMap);
            }
            Method method = methodMap.get(propertyName);
            if (method == null) {
                String methodName = getGetMethodName(propertyName);
                method = getGetMethod(c, methodName, null);
                if (method == null) {
                    methodName = getBooIsMethodName(propertyName);
                    method = getGetMethod(c, methodName, null);
                }
                methodMap.put(propertyName, method);
            }
            if (method != null) {
                try {
                    bean = method.invoke(bean, null);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                return null;
            }
        }
        return bean;
    }

    /**
     * 获取bean对象的获取属性值的Method方法对象
     * @param c
     * @param methodName
     * @param paras
     * @return
     */
    public static Method getGetMethod(Class c, String methodName, Class... paras) {
        try {
            Method m = c.getMethod(methodName, paras);
            m.setAccessible(true);
            return m;
        } catch (Exception e) {
            return null;
        }
    }

    /******************
     * 对象转换相关方法 *
     ******************/

    /**
     * 深度复制，使用序列化技术复制的整个对象
     */
    public static Serializable deeplyCopy(Serializable src) throws Exception {
        byte[] data = serialize(src);//获得对象的字节数组
        Serializable copy = (Serializable) unserialize(data);//从流中读取对象，此时该对象是一个与传入对象一样的全新的对象
        return copy ;
    }

    private static String dateFormat = "yyyy-MM-dd HH:mm:ss";
    private static SerializeConfig serializeConfig = new SerializeConfig();

    public static void setSerializeConfig(SerializeConfig serializeConfig) {
        ObjUtil.serializeConfig = serializeConfig;
    }

    static {
        serializeConfig.put(java.util.Date.class, new SimpleDateFormatSerializer(dateFormat));
        serializeConfig.put(java.sql.Timestamp.class, new SimpleDateFormatSerializer(dateFormat));
        serializeConfig.put(java.sql.Date.class, new SimpleDateFormatSerializer(dateFormat));
    }

    public static List<Map> toListMap(List listBean) {
        List<Map> list = new ArrayList<Map>();
        if (listBean != null && listBean.size() > 0) {
            String jsonArrayStr = JSONArray.toJSONString(listBean, serializeConfig);
            list = ((List<Map>) JSONArray.parseObject(jsonArrayStr, List.class));
        }
        return list;
    }

    public static Map beanToMap(Object dto) {
        Map map = new HashMap(1);
        if (dto != null) {
            String text = JSON.toJSONString(dto, serializeConfig, SerializerFeature.WriteMapNullValue);
            map = JSON.parseObject(text, Map.class);
        }
        return map;
    }

    public static <T> T mapToBean(Map map, Class<T> cls) {
        if (map != null) {
            String text = JSON.toJSONString(map, serializeConfig);
            return JSON.parseObject(text, cls);
        }
        return null;
    }

    /**
     * MapList转换为BeanList
     * @param listMap
     * @param cls
     * @param <T>
     * @return
     */
    public static<T> List<T> toBeanList(List<Map> listMap, Class<T> cls) {
        List<T> list = new ArrayList<T>();
        if (listMap != null) {
            for (Map m : listMap) {
                list.add(mapToBean(m, cls));
            }
        }
        return list;
    }

    public static <T> T convertObj(Object obj, Class<T> cls) {
        if (obj != null) {
            String text = JSON.toJSONString(obj, serializeConfig);
            return JSON.parseObject(text, cls);
        }
        return null;
    }

    /**
     * 序列化对象为字节数组
     * @param obj
     * @return
     * @throws IOException
     */
    public static byte[] serialize(Serializable obj) throws IOException {
        ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
        ObjectOutputStream objOutput = new ObjectOutputStream(byteOutput);
        objOutput.writeObject(obj);
        byte[] byteArray = byteOutput.toByteArray();
        byteOutput.close();
        objOutput.close();
        return byteArray;
    }

    /**
     * 反序列化字节数组为对象
     * @param bytes
     * @return 反序列的结果对象,操作异常返回null
     */
    public static Object unserialize(byte[] bytes) {
        try {
            ByteArrayInputStream byteInput = new ByteArrayInputStream(bytes);
            ObjectInputStream objInput = new ObjectInputStream(byteInput);
            Object obj = objInput.readObject();
            byteInput.close();
            objInput.close();
            return obj;
        } catch (Exception e) {
            return null;
        }
    }

    public static boolean equals(Object a, Object b) {
        return (a == b) || (a != null && a.equals(b));
    }

    public static class Diff{
        /**属性名称*/
        public String name;
        public Object oldValue;
        public Object newValue;

        public Diff(String name, Object oldValue, Object newValue) {
            this.name = name;
            this.oldValue = oldValue;
            this.newValue = newValue;
        }
    }

    /**
     * 比较两个对象有哪些不同的属性值，返回对比结果List
     * 可用于实现记录数据修改日志等功能
     * @param oldBean 旧值对象
     * @param newBean 新值对象
     * @param checkNull 是否检测空值
     * @return
     */
    public static List<Diff> diff(Object oldBean, Object newBean, boolean checkNull) {
        List<Diff> result = new ArrayList<Diff>();
        if (oldBean == newBean || oldBean == null || newBean == null) {
            return result;
        }
        Map map1 = beanToMap(oldBean);
        Map map2 = beanToMap(newBean);
        Set map1keys = map1.keySet();
        for (Object key : map1keys) {
            Object oldValue = map1.get(key);
            Object newValue = map2.get(key);
            if (checkNull || newValue != null) {
                if (!equals(oldValue, newValue)) {
                    result.add(new Diff(key.toString(), oldValue, newValue));
                }
            }
        }
        return result;
    }

    public static String dump(Object o) {
        if (o == null) {
            return "<null>";
        } else {
            try {
                return JSON.toJSONString(o, new SerializerFeature[]{SerializerFeature.PrettyFormat, SerializerFeature.WriteDateUseDateFormat});
            } catch (Exception var2) {
                return "<null>";
            }
        }
    }

}
